home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue66 / Construc / Refactor / Source / uRCBuilder.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  2000-11-12  |  17.4 KB  |  479 lines

  1. unit uRCBuilder;
  2. interface
  3. uses classes;
  4.  
  5. const cVersion = 'Version';
  6.  
  7. type
  8.   tMyBuf = array of byte;
  9.   pMyBuf = ^TmyBuf;
  10.  
  11.   TResourceHandlerMissingFileNotify = procedure(const filename, info : string) of object;
  12.  
  13.   TResourceTypeEnum = (reMajor, reMinor, reRelease, reBuild,
  14.                        reProductMajor, reProductMinor, reProductRelease, reProductBuild,
  15.                        reAppid, reDescription, reCompany, reComments, reInternalName, reLegalCopyright,
  16.                        reLegalTrademarks, reProductName, reOriginalFilename, reIconFileName);
  17.   TResourceSettingSourceEnum = (rsEmpty, rsProject, rsFile, rsExecutable);
  18.  
  19.  
  20.   TResourceHandler = class(tobject)
  21.   private
  22.     fResourceSource : string;
  23.     fIcondata : pointer;
  24.     flist : tstringlist;
  25.     fOnResourceError : TResourceHandlerMissingFileNotify;
  26.     fRCMAskFilename: string;
  27.     fRCFileName: string;
  28.     fMask : tstringlist;
  29.     fsuccess: boolean;
  30.     function ReadAndTranslateIconFile(var aIconAsString : string) : boolean;
  31.     function getVersionInfo(index: TResourceTypeEnum): string;
  32.     procedure setVersionInfo(index: TResourceTypeEnum;
  33.       const Value: string);
  34.     function getVersionSource(
  35.       Index: TResourceTypeEnum): TResourceSettingSourceEnum;
  36.     procedure setVersionSource(Index: TResourceTypeEnum;
  37.       const Value: TResourceSettingSourceEnum);
  38.     Procedure setVersionInfoAndSource(const WhichOne : TResourceTypeEnum;  const aValue : string; const aSource : TResourceSettingSourceEnum);
  39.     function getResourceName(index: TResourceTypeEnum): string;
  40.     procedure SetBlanksToZero;
  41.     function DoesIconFileExist(var aFileName: string): boolean;
  42.   public
  43.     constructor create(aComponent : tcomponent); //override; overload;
  44.     destructor destroy; override;
  45.     Procedure PrepareResourceData(const TargetFileName, GeneralSettingsFilename : string);
  46.     procedure DoFileError(const fn, info : string);
  47.     Procedure GetSettings(const PathToIni : string; FromWhere : TResourceSettingSourceEnum);
  48.     Procedure SaveSettings(const PathToIni : string; aSection : string = cVersion);
  49.     Procedure ClearSettings;
  50.     procedure WriteRCFile;
  51.     Function DisplayRCData(aTitle : string): string;
  52.     Function VersionSummary: string;
  53.     procedure GetVersionInfoFromProgram(aFilename: string);
  54.     Property RCMaskFileName : string read fRCMaskFileName write fRCMaskFileName;
  55.     Property RCfileName : string read fRCfileName write fRCfileName;
  56.     Property ResourceName[index : TResourceTypeEnum] :  string read getResourceName;
  57.     Property VersionInfo[index : TResourceTypeEnum] : string read getVersionInfo write setVersionInfo;
  58.     Property VersionSource[Index : TResourceTypeEnum] : TResourceSettingSourceEnum read getVersionSource Write setVersionSource;
  59.     Property Success : boolean read fsuccess write fsuccess;
  60.     Property Mask : tstringlist read fMask write fMask;
  61.   published
  62.     Property OnResourceError : TResourceHandlerMissingFileNotify read fOnResourceError write fOnResourceError;
  63.     end;
  64.  
  65.  
  66.  
  67. implementation
  68. uses sysutils, inifiles, typinfo, uVersionInformation;
  69.  
  70.  
  71. { TResourceHandler }
  72.  
  73. destructor TResourceHandler.destroy;
  74. begin
  75.   if fIconData <> nil
  76.     then freemem(fIconData);
  77.   flist.free;
  78.   fMAsk.free;
  79.   inherited;
  80. end;
  81.  
  82. function TResourceHandler.DoesIconFileExist(var aFileName : string): boolean;
  83. { check to see if file as listed in options exists in current dir, otherwise create full path and look for it}
  84. { Set the current dir to the dir where the .rc file lives first}
  85. begin
  86.   aFileName := VersionInfo[reIconFileName];
  87.   if not FileExists(aFileName)
  88.     then begin
  89.       if not FileExists(IncludeTrailingBackslash(extractFilePath(fRCFileName))+ aFileName)
  90.         then begin
  91.           DoFileError(aFileName, ' Icon File for '+ extractFileName(fRCFileName)+' not found. Will compile without icon.');
  92.           result := false;
  93.           fSuccess := true;
  94.           exit;
  95.           end
  96.         else begin
  97.           aFileName := IncludeTrailingBackslash(extractFilePath(fRCFileName))+aFileName;
  98.           result := true;
  99.           end;
  100.       end
  101.     else begin
  102.       result := true;
  103.       end;
  104. end;
  105.  
  106. function TResourceHandler.ReadAndTranslateIconFile(var aIconAsString : string) : boolean;
  107. var i  : integer;
  108.     iconFileName, hexline : string;
  109.     MemStream : TMemoryStream;
  110.     p : byte;
  111. begin
  112.   result := true;
  113.   MemStream := TMemoryStream.create;
  114.   try
  115.     result := DoesIconFileExist(IconFileName);
  116.     if not result
  117.       then exit;
  118.     MemStream.LoadFromFile(IconFileName);
  119.     hexline := '';
  120.     aIconAsString := '';
  121.     memstream.Seek(0, soFromBeginning);
  122.     for i := 1 to MemStream.size do begin
  123.       memstream.read(p, 1);
  124.       hexline := hexline + IntToHex(Integer(p), 2)+' ';
  125.       if (i <> 0) and ( (i mod 16) = 0)
  126.         then begin
  127.          setlength(hexline, length(hexline)-1);
  128.          hexline := ''''+hexline+'''';
  129.          aIconAsString := aIconAsString + hexline+#13#10;
  130.          hexline := '';
  131.          end;
  132.       end;
  133.     // pick up the last part of the file if not all lines had 16 bytes
  134.     setlength(hexline, length(hexline)-1);
  135.     hexline := ''''+hexline+'''';
  136.     aIconAsString := aIconAsString + hexline;
  137.     If aIconAsString[length(aIconAsString)-1] = #10
  138.       then setLength(aIconAsString, length(aIconAsString)-2)
  139.       else setLength(aIconAsString, length(aIconAsString));
  140.   finally
  141.     Memstream.free;
  142.   end;
  143. end;
  144.  
  145.  
  146. procedure TResourceHandler.WriteRCFile;
  147. var tmp : string;
  148.     tmpsl : tstringlist;
  149.     IconData : string;
  150.     i, MainIconLine : integer;
  151.  
  152.   Function MakeSureDigitPresent(aString : string): string;
  153.   begin
  154.     if trim(astring) = ''
  155.       then result := '0'
  156.       else result := aString;
  157.   end;
  158.  
  159.  
  160. begin
  161.   fSuccess := true;
  162.   Tmpsl := tstringlist.create;
  163.   try
  164. {  if not fileExists(fRCMaskFileName)
  165.     then if assigned(fOnResourceError)
  166.         then begin
  167.            fsuccess := false;
  168.            DoFileError(fRcMaskFileName, 'RC Mask file not found.');
  169.            exit;
  170.            end;
  171.   tmpsl.LoadFromFile(fRCMaskFileName);
  172.   tmp := tmpsl.text;
  173.   }
  174.   if fMask.count = 0
  175.     then if assigned(fOnResourceError)
  176.         then begin
  177.            fsuccess := false;
  178.            DoFileError(fRcMaskFileName, 'Resource Mask empty.');
  179.            exit;
  180.            end;
  181.   tmpsl.assign(fMask);
  182.   tmp := tmpsl.text;
  183.   if ReadAndTranslateIconFile(IconData)
  184.     then tmp := stringReplace(tmp, '#ICONDATA#', IconData, [])
  185.     else begin
  186.       MainIconLine := -1;
  187.       for i := 0 to tmpsl.count-1 do
  188.          if pos('MAINICON', tmpsl[i]) > 0
  189.             then begin
  190.               MainIconLine := i;
  191.               break;
  192.               end;
  193.       if MainIconLine = -1
  194.         then begin
  195.            DoFileError(fRcMaskFileName, 'RC Mask file doesn''t contain MAINICON section.');
  196.            fsuccess := false;
  197.            exit;
  198.            end;
  199.       tmpsl.delete(MainIconLine);
  200.       tmpsl.delete(MainIconLine);
  201.       tmpsl.delete(MainIconLine);
  202.       tmpsl.delete(MainIconLine);
  203.       tmp := tmpsl.text;
  204.       end;
  205.   SetBlanksToZero;
  206.   tmp := stringReplace(tmp, '#Major#', MakeSureDigitPresent(VersionInfo[reMajor]), [rfReplaceAll]);
  207.   tmp := stringReplace(tmp, '#Minor#', MakeSureDigitPresent(VersionInfo[reMinor]), [rfReplaceAll]);
  208.   tmp := stringReplace(tmp, '#Release#', MakeSureDigitPresent(VersionInfo[reRelease]), [rfReplaceAll]);
  209.   tmp := stringReplace(tmp, '#Build#', MakeSureDigitPresent(VersionInfo[reBuild]), [rfReplaceAll]);
  210.   tmp := stringReplace(tmp, '#AppID#', MakeSureDigitPresent(VersionInfo[reAppID]), [rfReplaceAll]);
  211.   tmp := stringReplace(tmp, '#ProductMajor#', MakeSureDigitPresent(VersionInfo[reProductMajor]), [rfReplaceAll]);
  212.   tmp := stringReplace(tmp, '#ProductMinor#', MakeSureDigitPresent(VersionInfo[reProductMinor]), [rfReplaceAll]);
  213.   tmp := stringReplace(tmp, '#ProductRelease#', MakeSureDigitPresent(VersionInfo[reProductRelease]), [rfReplaceAll]);
  214.   tmp := stringReplace(tmp, '#ProductBuild#', MakeSureDigitPresent(VersionInfo[reProductBuild]), [rfReplaceAll]);
  215.  
  216.   tmp := stringReplace(tmp, '#Company#', VersionInfo[reCompany], [rfReplaceAll]);
  217.   tmp := stringReplace(tmp, '#Description#', VersionInfo[reDescription], [rfReplaceAll]);
  218.   tmp := stringReplace(tmp, '#InternalName#', VersionInfo[reInternalName], [rfReplaceAll]);
  219.   tmp := stringReplace(tmp, '#LegalCopyright#', VersionInfo[reLegalCopyRight], [rfReplaceAll]);
  220.   tmp := stringReplace(tmp, '#LegalTrademarks#', VersionInfo[reLegalTrademarks], [rfReplaceAll]);
  221.   tmp := stringReplace(tmp, '#OriginalFilename#', VersionInfo[reOriginalFilename], [rfReplaceAll]);
  222.   tmp := stringReplace(tmp, '#ProductName#', VersionInfo[reProductName], [rfReplaceAll]);
  223.   tmp := stringReplace(tmp, '#Comments#', VersionInfo[reComments], [rfReplaceAll]);
  224.   tmpsl.text := tmp;
  225.   try
  226.     tmpsl.SaveToFile(fRCFileName);
  227.   except
  228.     on e:exception do begin
  229.       DoFileError(fRCFileName, 'Could not save '+fRCFileName+': '+e.message);
  230.       fsuccess := false;
  231.       end;
  232.   end;
  233.   finally
  234.     tmpsl.free;
  235.   end;
  236. end;
  237.  
  238.  
  239. //DONE: when reading settings, set object to tResourceSettingSourceEnum
  240. procedure TResourceHandler.GetSettings(const PathToIni : string; FromWhere : TResourceSettingSourceEnum);
  241. var i : TResourceTypeEnum;
  242. begin
  243.   if FileExists(PathToIni)
  244.     then with tinifile.create(PathToIni) do begin
  245.       fResourceSource := filename;
  246.       for i := low(i) to high(i) do
  247.         if (VersionInfo[i] = '0') or (VersionInfo[i] = '')
  248.           then SetVersionInfoAndSource(i, readString(cVersion, ResourceName[i], VersionInfo[i]), FromWhere);
  249.       UpdateFile;
  250.       free;
  251.       end
  252.     else If (FromWhere = rsFile)
  253.        then begin
  254.          DoFileError(PathToIni, 'Did not exist, trying to read version info from .exe');
  255.          fResourceSource := 'From Executable';
  256.          try
  257.            GetVersionInfoFromProgram(ChangeFileExt(PathToIni, '.exe'));
  258.          except
  259.            on e:exception do begin
  260.            DoFileError(ChangeFileExt(PathToIni, '.exe'), 'Unable to read version info from exe, '+e.message);
  261.            end;
  262.          end;
  263.          end;
  264. end;
  265.  
  266. procedure TResourceHandler.ClearSettings;
  267. var i : TResourceTypeEnum;
  268. begin
  269.   flist.Clear;
  270.   for i := reMajor to reAppid do
  271.      flist.addobject('0', tobject(rsEmpty));
  272.   for i := reDescription to reIconFileName do
  273.      flist.addobject('', tobject(rsEmpty));
  274. end;
  275.  
  276. procedure TResourceHandler.SaveSettings(const PathToIni: string; aSection : string = cVersion);
  277. var i : TResourceTypeEnum;
  278. begin
  279.   try
  280.   with tinifile.create(PathToIni) do begin
  281.      for i := low(i) to high(i) do
  282.        WriteString(aSection, ResourceName[i], VersionInfo[i]);
  283.      UpdateFile;
  284.      free;
  285.      end;
  286.   except
  287.     on e:exception do begin
  288.       DoFileError('Problem saving '+PathToIni, e.message);
  289.       end;
  290.     end;
  291.  
  292. end;
  293.  
  294.  
  295. function TResourceHandler.DisplayRCData(aTitle : string): string;
  296. var i : TResourceTypeEnum;
  297. begin
  298.   Result := aTitle+ #13#10; //'RC settings from '+fResourceSource + #13#10;
  299.   Result := Result + 'FileVersion = ' + VersionInfo[reMajor]+'.'+VersionInfo[reMinor]+'.'+VersionInfo[reRelease]+'.'+VersionInfo[reBuild]+#13#10;
  300.   Result := Result + 'ProductVersion = ' + VersionInfo[reProductMajor]+'.'+VersionInfo[reProductMinor]+'.'+VersionInfo[reProductRelease]+'.'+VersionInfo[reProductBuild]+#13#10;
  301.   for i := reAppid to reIconFileName do
  302.     result := result + ResourceName[i]+' = ' + VersionInfo[i]+#13#10;
  303. end;
  304.  
  305. constructor TResourceHandler.create(aComponent: tcomponent);
  306. var i : TResourceTypeEnum;
  307. begin
  308. //  inherited;
  309.   flist := tstringlist.create;
  310.   fMask := tstringlist.create;
  311.   for i := low(i) to high(i) do
  312.     flist.add('');
  313. end;
  314.  
  315. function TResourceHandler.getVersionInfo(
  316.   index: TResourceTypeEnum): string;
  317. begin
  318.   result := flist[ord(index)];
  319.   if (result = '')
  320.       and
  321.      ( (index = reBuild)
  322.         or
  323.        (index = reProductBuild))
  324.     then result := '0';
  325. end;
  326.  
  327. procedure TResourceHandler.setVersionInfo(index: TResourceTypeEnum;
  328.   const Value: string);
  329. begin
  330.  flist[ord(index)] := value;
  331. end;
  332.  
  333. function TResourceHandler.getVersionSource(
  334.   Index: TResourceTypeEnum): TResourceSettingSourceEnum;
  335. begin
  336.   result := TResourceSettingSourceEnum(flist.objects[ord(index)]);
  337. end;
  338.  
  339. procedure TResourceHandler.setVersionSource(Index: TResourceTypeEnum;
  340.   const Value: TResourceSettingSourceEnum);
  341. begin
  342.   flist.objects[ord(index)] := tobject(ord(value));
  343. end;
  344.  
  345.  
  346. procedure TResourceHandler.setVersionInfoAndSource(
  347.   const WhichOne: TResourceTypeEnum; const aValue: string;
  348.   const aSource: TResourceSettingSourceEnum);
  349. begin
  350.   setVersionInfo(WhichOne, aValue);
  351.   setVersionSource(WhichOne, aSource);
  352.  
  353. end;
  354.  
  355. function TResourceHandler.getResourceName(index: TResourceTypeEnum): string;
  356. var  EnumType : PTypeInfo;
  357. begin
  358.   EnumType := TypeInfo(TResourceTypeEnum);
  359.   result := GetEnumName(EnumType, ord(index) );
  360.   system.delete(result, 1, 2);
  361. end;
  362.  
  363. Procedure TResourceHandler.GetVersionInfoFromProgram(aFilename : string);
  364.   function getNthNumber(n : integer; const tmp : string): string;
  365.   var i, startpos : integer;
  366.       found : boolean;
  367.   begin
  368.     result := tmp;
  369.     case n of
  370.       1 : begin
  371.            system.delete(result, pos('.', result),100);
  372.            end;
  373.       2 : begin
  374.             startpos := 1;
  375.             for i := 1 to length(tmp) do
  376.               if tmp[i] = '.'
  377.                 then begin
  378.                   startpos := i;
  379.                   break;
  380.                   end;
  381.             system.delete(result, 1, startpos);
  382.             system.delete(result, pos('.', result),100);
  383.             end;
  384.       3 : begin
  385.             found := false;
  386.             startpos := 1;
  387.             for i := 1 to length(tmp) do begin
  388.               if not found and (tmp[i] = '.')
  389.                 then begin
  390.                   found := true;
  391.                   continue;
  392.                   end;
  393.               if found and (tmp[i] = '.')
  394.                 then begin
  395.                    startpos := i;
  396.                    break;
  397.                    end;
  398.               end;
  399.             system.delete(result, 1, startpos);
  400.             system.delete(result, pos('.', result),100);
  401.             end;
  402.       4 : begin
  403.             startpos := 1;
  404.             for i := length(tmp) downto 1 do
  405.             if tmp[i] = '.'
  406.               then begin
  407.                 startpos := i;
  408.                 break;
  409.                 end;
  410.             system.delete(result, 1, startpos);
  411.             end;
  412.       end; // case
  413.    end;
  414.  
  415. begin
  416.   with TVersionInformation.instance do begin
  417.     FileName := aFileName;
  418.     if HasVerInfo
  419.       then begin
  420.         setVersionInfoAndSource(reMajor, getNthNumber(1, Values[MSVerNames[msFileVersion]]), rsExecutable);
  421.         setVersionInfoAndSource(reMinor, getNthNumber(2, Values[MSVerNames[msFileVersion]]), rsExecutable);
  422.         setVersionInfoAndSource(reRelease, getNthNumber(3, Values[MSVerNames[msFileVersion]]), rsExecutable);
  423.         setVersionInfoAndSource(reBuild, getNthNumber(4, Values[MSVerNames[msFileVersion]]), rsExecutable);
  424.         setVersionInfoAndSource(reProductMajor, getNthNumber(1, Values[MSVerNames[msProductVersion]]), rsExecutable);
  425.         setVersionInfoAndSource(reProductMinor, getNthNumber(2, Values[MSVerNames[msProductVersion]]), rsExecutable);
  426.         setVersionInfoAndSource(reProductRelease, getNthNumber(3, Values[MSVerNames[msProductVersion]]), rsExecutable);
  427.         setVersionInfoAndSource(reProductBuild, getNthNumber(4, Values[MSVerNames[msProductVersion]]), rsExecutable);
  428.         setVersionInfoAndSource(reAppID,'AppID', rsExecutable);
  429.  
  430.         setVersionInfoAndSource(reCompany, Values[MSVerNames[msCompanyName]], rsExecutable);
  431.         setVersionInfoAndSource(reDescription, Values[MSVerNames[msFileDescription]], rsExecutable);
  432.         setVersionInfoAndSource(reInternalName, Values[MSVerNames[msInternalName]], rsExecutable);
  433.         setVersionInfoAndSource(reLegalCopyRight, Values[MSVerNames[msLegalCopyRight]], rsExecutable);
  434.         setVersionInfoAndSource(reLegalTrademarks, Values[MSVerNames[msLegalTrademarks]], rsExecutable);
  435.         setVersionInfoAndSource(reOriginalFileName, Values[MSVerNames[msOriginalFileName]], rsExecutable);
  436.         setVersionInfoAndSource(reProductName, Values[MSVerNames[msProductName]], rsExecutable);
  437.         setVersionInfoAndSource(reComments, Values[MSVerNames[msComments]], rsExecutable);
  438.         end
  439.       else begin
  440.         DoFileError(aFileName, ErrorMessage);
  441.         end;
  442.     end;
  443. end;
  444.  
  445.  
  446.  
  447. procedure TResourceHandler.SetBlanksToZero;
  448. var i : TResourceTypeEnum;
  449. begin
  450.   for i := reMajor to reAppid do
  451.     if flist[ord(i)] = '0'
  452.       then flist[ord(i)] := '0';
  453. end;
  454.  
  455. procedure TResourceHandler.DoFileError(const fn, info: string);
  456. begin
  457.   if assigned(fOnResourceError)
  458.     then fOnResourceError(fn, info);
  459. end;
  460.  
  461. procedure TResourceHandler.PrepareResourceData(const TargetFileName,
  462.   GeneralSettingsFilename: string);
  463. begin
  464.   ClearSettings;
  465.   GetSettings(GeneralSettingsFilename, rsProject);
  466.   GetSettings(TargetFileName, rsFile);
  467. end;
  468.  
  469. function TResourceHandler.VersionSummary: string;
  470. var i : TResourceTypeEnum;
  471. begin
  472.   Result := 'VerInfo: ' + VersionInfo[reMajor]+'.'+VersionInfo[reMinor]+'.'+VersionInfo[reRelease]+'.'+VersionInfo[reBuild]+'; '+
  473.              VersionInfo[reProductMajor]+'.'+VersionInfo[reProductMinor]+'.'+VersionInfo[reProductRelease]+'.'+VersionInfo[reProductBuild]+'; ';
  474.   for i := reAppid to reOriginalFilename do
  475.     result := result + VersionInfo[i]+'; ';
  476. end;
  477.  
  478. end.
  479.